iT邦幫忙

2026 iThome 鐵人賽

DAY 14
0
Kubernetes

不是背 YAML!30 天從零打造 Kubernetes 微服務:從本機實戰一路到 CKA系列 第 14

Day 14|Running 不代表健康:Liveness、Readiness、Requests 與 Limits

  • 分享至 

  • xImage
  •  

今天主要都著重在觀念介紹!
就沒有那麼多的實際操作啦
所以今天可以快速看過!

廢話不多說,進入今天的主題!

目前!!

我們的 Application 已經不像一開始只有一個單純的 FastAPI,而是開始依賴其他服務:

API
├── Redis
└── PostgreSQL

這時候就會出現一個很重要的問題。

假設 FastAPI 本身沒有掛掉,Uvicorn Process 也還正常執行,但是 Redis 突然無法連線:

FastAPI:正常
Redis:掛掉

Kubernetes 可能還是會看到:

Pod = Running

但這個 API 真的算「健康」嗎?

答案是不一定。

因為如果你的 API 處理 Request 時一定要使用 Redis,那 Redis 掛掉之後,即使 Python Process 還活著,使用者的 Request 仍然可能全部失敗。

所以今天要理解的核心,就是:

Pod 活著

跟:

Pod 可以正常提供服務

其實是兩件不同的事情。


Running 不代表 Application 可以正常工作

先理解 Kubernetes 裡面一個非常重要的觀念:

STATUS = Running

主要是在告訴你:

Container 裡面的主要 Process 還在執行。

例如我們的 Container 執行:

uvicorn app.main:app

只要 Uvicorn 還沒有 Crash,Container 就可能維持:

Running

但是 Application 背後可能已經發生:

Redis 無法連線
PostgreSQL 無法連線
外部 API 無法使用

也就是說:

Container Process 還活著
≠
Application 可以正常提供服務

這也是 Kubernetes 為什麼除了 Container 是否正在 Running 之外,還需要額外判斷:

Liveness
Readiness

可以先把它們記成:

Liveness
→ 你還活著嗎?

Readiness
→ 你現在可以工作嗎?

這兩個問題看起來很像,但用途完全不同。


Liveness Probe:這個 Container 還活著嗎?

Liveness Probe 處理的是:

這個 Application 是否已經壞到需要重新啟動?

我們之前 FastAPI 已經準備過:

@app.get("/health/live")
def live():

    return {
        "status": "alive"
    }

接著可以在 Deployment 裡加入:

livenessProbe:
  httpGet:
    path: /health/live
    port: 8000

  initialDelaySeconds: 10
  periodSeconds: 10

意思是 Container 啟動之後,Kubernetes 先等:

10 秒

然後每:

10 秒

向:

/health/live

送一次 HTTP Request。

正常情況下:

GET /health/live
↓
200 OK
↓
Kubernetes 認為 Application 還活著

但如果 Application 發生 Deadlock、程式完全卡住,或 HTTP Server 已經無法正常回應:

GET /health/live
↓
失敗
↓
再次檢查
↓
持續失敗

達到失敗條件之後,kubelet 就可能重新啟動這個 Container。

所以 Liveness Probe 的主要目的不是決定「要不要把流量送給它」,而是決定:

這個 Container
需不需要被重新啟動?

這一點非常重要。

因此一般來說,不要把太多外部 Dependency 塞進 Liveness Probe。

例如:

Redis 掛掉
↓
Liveness 失敗
↓
API 被 Restart
↓
Redis 還是掛掉
↓
API 又 Restart

這樣重新啟動 API 根本沒有解決問題。

因此比較常見的設計是:

Liveness
→ 確認 Application 自己有沒有活著

Readiness
→ 確認 Application 現在能不能正常工作

Readiness Probe:現在可以把流量送給你嗎?

接著就是今天最重要的 Readiness Probe。

Readiness 問的問題不是:

你有沒有活著?

而是:

你現在準備好接收使用者 Request 了嗎?

例如我們的 API 需要 Redis 才能正常工作,那就可以增加:

@app.get("/health/ready")
def ready():

    redis_client.ping()

    return {
        "status": "ready"
    }

這段程式非常簡單。

Kubernetes 呼叫:

GET /health/ready

FastAPI 就會先執行:

redis_client.ping()

如果 Redis 正常:

Redis PING 成功
↓
回傳 200
↓
Pod Ready

如果 Redis 掛掉:

Redis PING 失敗
↓
Endpoint 回傳錯誤
↓
Readiness Probe 失敗
↓
Pod Not Ready

Deployment 裡加入:

readinessProbe:
  httpGet:
    path: /health/ready
    port: 8000

  initialDelaySeconds: 5
  periodSeconds: 5

代表 Container 啟動五秒之後,Kubernetes 每五秒檢查一次:

/health/ready

Readiness 失敗不代表 Container 會被殺掉

這是 Liveness 和 Readiness 最大的差異之一。

假設:

Readiness Probe 失敗

通常 Kubernetes 不會因為這件事情直接 Restart Container。

而是把 Pod 標記成:

Not Ready

例如原本:

READY   STATUS
1/1     Running

可能變成:

READY   STATUS
0/1     Running

注意這個畫面非常重要:

0/1 Running

乍看好像很奇怪。

既然:

STATUS = Running

為什麼:

READY = 0/1

原因就是:

Running
→ Process 還活著

Ready
→ Kubernetes 認為它可以接流量

這個 Pod 的 Uvicorn Process 還活著,所以它仍然是:

Running

但是 Readiness Probe 沒有通過,所以 Kubernetes 認為:

你現在不能提供服務。

Service 會利用 Readiness 決定要不要把流量送過去

Readiness 真正重要的地方,會出現在 Service。

假設我們現在有三個 API Pod:

Service
   │
   ├── Pod A Ready
   ├── Pod B Ready
   └── Pod C Not Ready

正常情況下,Service 應該只把流量送給:

Pod A
Pod B

而不是:

Pod C

所以 Readiness Probe 可以看成 Kubernetes 幫我們建立的一道保護機制:

Application 暫時出問題
↓
Readiness Failed
↓
Pod 仍然活著
↓
暫時不要接收正常流量

等 Redis 恢復之後:

Redis 恢復
↓
/health/ready 又回傳成功
↓
Pod 重新 Ready
↓
Service 又可以把流量送過來

不一定需要重新建立 Pod。


故意把 Readiness 寫錯,觀察 Kubernetes 的行為

學 Kubernetes 很重要的一個方法就是:

故意把東西弄壞。

例如我們原本:

readinessProbe:
  httpGet:
    path: /health/ready
    port: 8000

故意改成不存在的:

readinessProbe:
  httpGet:
    path: /health/wrong
    port: 8000

重新 Apply:

kubectl apply -f k8s/

接著查看:

kubectl get pods -n cka-lab

可能看到:

NAME                       READY   STATUS    RESTARTS
api-xxxxxxxxxx-xxxxx       0/1     Running   0

這個結果非常值得理解。

STATUS = Running

代表:

Uvicorn 還活著。

但是:

READY = 0/1

代表:

Readiness Probe 沒有通過。

也就是:

Process 活著

但是

Kubernetes 不認為它現在可以服務。

這就是 Running 與 Ready 最直接的差異。


kubectl describe:不要一出問題就重新啟動

看到:

0/1 Running

之後,不應該第一個反應就是:

重開 Docker

或:

刪掉 Pod 看看。

更好的做法是先找原因。

例如:

kubectl describe pod API_POD -n cka-lab

往下看 Events,很可能看到類似:

Readiness probe failed

或者:

HTTP probe failed with statuscode: 404

這時候就可以推理:

Pod Running
↓
但是 Ready 0/1
↓
查看 describe
↓
Readiness Probe Failed
↓
檢查 path / port / Application

接著才發現:

/health/wrong

根本不存在。

這才是真正在 Kubernetes 裡面的 Troubleshooting 流程。

而不是:

不知道發生什麼事
↓
重開
↓
好了
↓
但不知道為什麼

Resource Requests:這個 Pod 至少需要多少資源?

除了 Health Check,今天還有另一個非常重要的 Kubernetes 概念:

Resources

一個 Kubernetes Cluster 裡面可能同時跑很多 Pod。

例如:

Node
├── API Pod
├── Redis Pod
├── PostgreSQL Pod
├── Worker Pod
└── 其他 Pod

Scheduler 在決定:

新的 Pod 要放在哪一台 Node?

就必須知道:

這個 Pod 大概需要多少資源?

因此可以在 Deployment 裡設定:

resources:
  requests:
    cpu: 100m
    memory: 128Mi

Request 可以先理解成:

這個 Container 希望 Kubernetes 至少替它保留多少資源。


CPU 的 m 是什麼?

Kubernetes CPU 很常看到:

100m
250m
500m

這裡的:

m

代表:

millicpu

可以這樣理解:

1000m = 1 CPU Core

500m = 0.5 Core

100m = 0.1 Core

因此:

cpu: 100m

表示這個 Container Request:

0.1 CPU Core

Memory 的 Mi 是什麼?

例如:

memory: 128Mi

可以先簡單理解成:

大約 128 MiB Memory

所以:

requests:
  cpu: 100m
  memory: 128Mi

可以想成這個 Pod 在跟 Kubernetes 說:

我要執行的話,
請至少幫我準備:

CPU:0.1 Core
Memory:128Mi

Request 會影響 Scheduler

這裡有一個非常容易誤解的地方。

Requests 並不是單純:

寫給人看的建議值。

Scheduler 真的會使用它。

假設現在有:

Node A
剩餘可分配 Memory:500Mi

你建立一個 Pod:

resources:
  requests:
    memory: 1Gi

Scheduler 看到的是:

這個 Pod 宣告需要 1Gi

但是:

Node A 只剩 500Mi

所以它可能直接判斷:

放不下。

結果 Pod 就可能停留在:

Pending

即使你打開電腦監控工具,發現:

實際 RAM 明明還有很多。

還是有可能排程失敗。

原因是 Scheduler 不會單純根據:

現在 Application 實際用了多少 RAM

來做 Scheduling。

它會根據 Pod 宣告的:

Resource Requests

判斷 Node 是否還有足夠的可分配資源。

因此可以把 Request 想成:

Scheduling 時的資源需求。

Resource Limits:最多允許你使用多少資源?

Request 處理的是:

我至少需要多少。

而 Limit 處理的是:

我最多可以使用多少。

例如:

resources:
  requests:
    cpu: 100m
    memory: 128Mi

  limits:
    cpu: 500m
    memory: 256Mi

這代表:

CPU Request:100m
CPU Limit:500m

Memory Request:128Mi
Memory Limit:256Mi

可以簡化理解成:

Request
→ Scheduler 排程時的重要依據

Limit
→ Container 執行時不能無限制使用資源

CPU 超過 Limit 會怎樣?

例如:

limits:
  cpu: 500m

代表 CPU 使用量受到:

0.5 Core

的限制。

如果 Application 很忙,想使用更多 CPU,Linux / Kubernetes 可能會進行:

CPU Throttling

簡單來說就是:

你想跑更快
↓
但是你的 CPU Limit 只有 500m
↓
系統限制 CPU 使用時間
↓
Application 變慢

但通常不會只是因為超過 CPU Limit 就直接把 Container 殺掉。


Memory 超過 Limit 會怎樣?

Memory 的行為就不一樣。

假設:

limits:
  memory: 256Mi

但是你的 Application 持續增加 Memory:

150Mi
↓
200Mi
↓
250Mi
↓
300Mi

超過 Memory Limit 之後,Container 有可能因為記憶體不足被終止。

這時候你可能看到:

OOMKilled

OOM 就是:

Out Of Memory

所以以後看到:

OOMKilled

第一個應該想到的方向就是:

Application 實際 Memory Usage
        VS
Container Memory Limit

可能是:

Memory Limit 設太低

也可能是:

Application Memory Leak

而不是看到 Pod Restart 就直接認為:

Kubernetes 壞掉了。

Requests 與 Limits 可以這樣理解

假設某個 API 設定:

resources:
  requests:
    cpu: 100m
    memory: 128Mi

  limits:
    cpu: 500m
    memory: 256Mi

可以把整件事情理解成:

Scheduling 階段

Pod:
「我要至少 100m CPU、128Mi Memory。」

Scheduler:
「哪一台 Node 有能力容納你?」

Pod 被排到 Node 之後開始執行:

Runtime 階段

CPU 最多:
500m

Memory 最多:
256Mi

如果 CPU 嘗試使用超過限制:

可能被 Throttling (限制、節流)

如果 Memory 超過限制:

可能 OOMKilled

因此:

Requests
→ 主要影響 Scheduling

Limits
→ 主要限制 Runtime 資源使用

這是目前最值得先記住的差異。


把今天所有東西串起來

目前我們的架構已經變成:

Client
  │
  ▼
Service
  │
  ▼
API Pod
  │
  ├── Redis
  │
  └── PostgreSQL

Kubernetes 不只是要知道:

API Container 有沒有在跑。

還需要知道:

Application 是否健康?

現在是否可以接 Request?

需要多少 CPU?

需要多少 Memory?

最多允許使用多少資源?

所以今天加入兩組非常重要的機制。

第一組是:

Liveness Probe
Readiness Probe

負責 Application Health。

可以記成:

Liveness
→ 我還活著嗎?
→ 長期失敗可能 Restart Container

Readiness
→ 我現在可以工作嗎?
→ 失敗就先不要送流量給我

第二組則是:

Resource Requests
Resource Limits

負責資源管理。

可以記成:

Requests
→ 排程時,我需要多少資源

Limits
→ 執行時,我最多可以用多少資源

因此以後看到:

0/1 Running

不要只覺得「Pod 明明 Running 為什麼不能用」,而應該想到:

Readiness Probe

看到:

Pod Pending

除了 Image、PVC 等問題之外,也應該想到:

Resource Request 是否超過 Node 能提供的資源?

看到:

OOMKilled

則應該想到:

Memory Usage
vs
Memory Limit

到了這裡,我們開始不只是會:

把 Pod 跑起來

而是開始學真正 Kubernetes 在 Production 環境很重要的事情:

讓 Kubernetes 知道:

什麼時候 Application 是健康的,
什麼時候可以接流量,
什麼時候需要重新啟動,
以及這個 Application 到底需要多少資源。

明天開始!
要來做一個第一次 Kubernetes 綜合實戰演練
假裝來把系統弄壞,再來對應的 troubleshooting 如何做~!


上一篇
Day 13|加入 PostgreSQL 與 PVC:Pod 可以死,資料不能跟著死
下一篇
Day 15|第一次 Kubernetes 綜合實戰:今天不學新東西,只把系統弄壞
系列文
不是背 YAML!30 天從零打造 Kubernetes 微服務:從本機實戰一路到 CKA18
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言